﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.Globalization;
using System.IO;
using System.Xml.Linq;
using System.Collections;
using System.Xml;

namespace SlamCms.SharePoint.Core.Extensions
{
	public static class SPFeatureReceiverPropertiesExtensions
	{
		#region Public Methods
		public static void UpdateFilesInModule(this SPFeatureReceiverProperties instance)
		{
			instance.ExecuteInModules(UpdateFilesInModule, false);
		}

		public static void RemoveFilesFromModule(this SPFeatureReceiverProperties instance)
		{
			instance.ExecuteInModules(RemoveFilesFromModule, false);
		}

		public static void UpdateFilesInModule(this SPFeatureReceiverProperties instance, bool force)
		{
			instance.ExecuteInModules(UpdateFilesInModule, force);
		}

		public static void RemoveFilesFromModule(this SPFeatureReceiverProperties instance, bool force)
		{
			instance.ExecuteInModules(RemoveFilesFromModule, force);
		}
		#endregion

		#region Private Methods
		private static void ExecuteInModules(this SPFeatureReceiverProperties instance, Action<SPElementDefinition, SPWeb, Boolean> action, bool force)
		{
			SPWeb web;

			if (instance.Feature.Parent is SPSite)
			{
				SPSite site = (SPSite)instance.Feature.Parent;
				web = site.RootWeb;
			}
			else
			{
				web = (SPWeb)instance.Feature.Parent;
			}

			var ElementDefinitions = instance.Definition.GetElementDefinitions(CultureInfo.CurrentCulture);

			foreach (SPElementDefinition ElementDefinition in ElementDefinitions)
			{
				action.Invoke(ElementDefinition, web, force);
			}
		}

		private static void UpdateFilesInModule(SPElementDefinition elementDefinition, SPWeb web, bool force)
		{
			Module module = elementDefinition.GetModule();

			SPFile spFile = null;

			if (module == null)
			{
				return;
			}

			foreach (Module.File file in module.Files)
			{
				string physicalPath = Path.Combine(module.PhysicalPath, file.Path);
				string virtualPath = String.Concat(String.Concat(web.Url.TrimEnd('/'), "/", module.ProvisioningUrl.TrimEnd('/')).TrimEnd('/'), "/", file.Name);

				if (File.Exists(physicalPath))
				{
					using (StreamReader sreader = new StreamReader(physicalPath))
					{
						try
						{
							spFile = web.GetFile(virtualPath);

							if (spFile.Exists && !spFile.CheckOutFileStatus())
							{
								spFile.CheckOut();
							}

							spFile = web.Files.Add(virtualPath, sreader.BaseStream, new Hashtable(file.Properties), true);

							spFile.CheckIn("Updated", SPCheckinType.MajorCheckIn);

							if (spFile.Item.CheckContentApproval())
							{
								spFile.Approve("Updated");
							}

							spFile.Update();
						}
						catch (Exception ex)
						{
							if (!force)
							{
								throw new SPException(String.Format("Could not update the {0}", file.Name), ex);
							}
						}
					}
				}
			}

		}

		private static void RemoveFilesFromModule(SPElementDefinition elementDefinition, SPWeb web, bool force)
		{
			Module module = elementDefinition.GetModule();

			if (module == null)
			{
				return;
			}

			foreach (Module.File file in module.Files)
			{
				try
				{
					string virtualPath = String.Concat(String.Concat(web.Url.TrimEnd('/'), "/", module.ProvisioningUrl.TrimEnd('/')).TrimEnd('/'), "/", file.Name);

					SPFile spFile = web.GetFile(virtualPath);

					if (spFile.Exists)
					{
						if (spFile.CheckOutFileStatus())
						{
							web.GetFile(virtualPath).CheckOut();
						}

						spFile.Delete();

						spFile.Update();
					}
				}
				catch (Exception ex)
				{
					if (!force)
					{
						throw new SPException(String.Format("Could not remove the {0}", file.Name), ex);
					}
				}
			}

		}

		private static Module GetModule(this SPElementDefinition elementDefinition)
		{
			if (elementDefinition.ElementType != "Module")
			{
				return null;
			}

			XElement xml = elementDefinition.XmlDefinition.ToXElement();

			XNamespace xmlns = "http://schemas.microsoft.com/sharepoint/";

			string featureDir = elementDefinition.FeatureDefinition.RootDirectory;

			Module module = (from m in xml.DescendantsAndSelf()
							 select new Module
							 {
								 ProvisioningUrl = m.Attributes("Url").FirstOrDefault() == null ? "" : m.Attribute("Url").Value,
								 PhysicalPath = Path.Combine(featureDir, m.Attributes("Path").FirstOrDefault() == null ? "" : m.Attribute("Path").Value),
								 Files = (from f in m.Elements(xmlns.GetName("File"))
										  select new Module.File
										  {
											  Name = f.Attributes("Url").FirstOrDefault() == null ? "" : f.Attribute("Url").Value,
											  Path = f.Attributes("Path").FirstOrDefault() == null ? "" : f.Attribute("Path").Value,
											  Properties = (from p in f.Elements(xmlns.GetName("Property"))
															select p).ToDictionary(
															  n => n.Attributes("Name").FirstOrDefault() == null ? "" : n.Attribute("Name").Value,
															  v => v.Attributes("Value").FirstOrDefault() == null ? "" : v.Attribute("Value").Value)
										  }).ToArray()
							 }).First();
			return module;
		}

		#endregion

		public static XElement ToXElement(this XmlNode node)
		{
			XDocument xDoc = new XDocument();

			using (XmlWriter xmlWriter = xDoc.CreateWriter())

				node.WriteTo(xmlWriter);

			return xDoc.Root;

		}
	}

	public class Module
	{
		public string ProvisioningUrl { get; set; }
		public string PhysicalPath { get; set; }
		public Module.File[] Files { get; set; }

		public class File
		{
			public string Name { get; set; }
			public string Path { get; set; }
			public Dictionary<string, string> Properties { get; set; }
		}
	}
}
